import SparkMD5 from 'spark-md5'
import { MESSAGE_MAP, FILE_TYPES, CHUNK_SIZE } from "./config"; // 配置信息
import * as utils from "./utils"; // 工具函数
import {upload, uploadChunks, mergeChunks, verifyChunk} from "./api"; // API
;(function uploadFile(doc) {
    const fileInput = doc.getElementById("file"); // 文件input
    const progressEl = doc.getElementById("upload-progress"); // 进度条
    const messageEl = doc.getElementById("message"); // 消息提示
    const uploadBtn = doc.getElementById("upload-btn"); // 上传按钮
    const UPLOAD_STATUS = {
        OFF: 1,
        PENDING: 2,
        PAUSE: 3,
        FINISH: 4,
        FAIL: 5
    }
    /**
     * 请求容器包装
     * @param {String} desc 请求接口描述
     * @param {Function} requestFn 请求函数，返回值为promise
     * @param {Object} params 参数
     * @return {Promise<any>}
     */
    const ApiWrapper = (desc, requestFn, params) => {
        const promiseResult = Promise.resolve(requestFn(params))// 确保requestFn返回的是个promise
        return promiseResult.then(res => {
            const resContent = res.data
            if (!resContent.code || resContent.code !== 200) {
                throw new Error(resContent.message || '异常')
            }
            return res.data.data
        }).catch(err => {
            console.warn(`${desc}, ${err.message}`)
            return Promise.reject(new Error(err))
        })
    }

    /**
     * 获取文件切片
     * @param {File} file
     * @param {Number} chunkSize
     * @return {Array<File>}
     */
    const getChunks = (file, chunkSize) => {
        let currentSize = 0;
        const chunks = [];
        while (currentSize < file.size) {
            chunks.push(file.slice(currentSize, currentSize + chunkSize));
            currentSize += chunkSize;
        }
        return chunks;
    }

    /**
     * 获取文件MD5值
     * @param {File} file
     * @param {Number} chunkSize
     * @return {Promise<String>}
     */
    const getFileMd5 = (file, chunkSize) => {
        return new Promise(resolve => {
            const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice
            const md5 = new SparkMD5();
            const fr = new FileReader();
            let index = 0;
            const loadFile = () => {
                const slice = blobSlice.call(file, index, index + chunkSize);
                fr.readAsBinaryString(slice);
            }
            loadFile();
            fr.onload = (e) => {
                md5.appendBinary(e.target.result);
                if (index < file.size) {
                    index += chunkSize;
                    loadFile()
                } else {
                    resolve(md5.end())
                }
            }
        })
    }

    /**
     * 渲染视频
     * @param path
     */
    const renderVideo = path => {

    }

    /**
     * 文件上传class
     * @createBy haor
     * @createTime 20220728
     */
    class FileChunkUpload {
        constructor(options) {
            this.init(options)
        }
        /**
         * 初始化
         * @param {Object} options
         */
        init(options) {
            this.bindEvent()
            this.setUploadStatus(UPLOAD_STATUS.OFF) // 默认为未上传
        }

        /**
         * 绑定事件
         */
        bindEvent() {
            uploadBtn.addEventListener("click", this.onUploadFile.bind(this));
        }

        /**
         * 分块计算文件的md5值
         * @param {*} file 文件
         * @returns {Promise}
         */
        calculateFileMd5ByDefaultChunkSize(file) {
            return getFileMd5(file, CHUNK_SIZE).then(res => res.toUpperCase()).catch(() => {
                return Promise.resolve(null)
            })
        }

        /**
         * 设置上传状态
         * @param status
         */
        setUploadStatus(status) {
            this.status = status
        }

        /**
         * 验证文件
         * @param {File} file
         */
        validateFile(file) {
            const suffix = utils.getFileSuffix(file.name);
            if (FILE_TYPES.includes(suffix)) return null
            return MESSAGE_MAP.INVALID_TYPE;
        }

        /**
         * 提交文件
         * @param file
         * @param index
         * @param hash
         * @param chunkSize
         * @return {Promise<unknown>}
         */
        async postFile(file, index, hash, chunkSize) {
            const name = file.name; // 文件名称
            const size = file.size; // 文件大小
            const chunkCount = Math.ceil(size / chunkSize); // 总切片数
            if (index > chunkCount) return
            const start = chunkSize * index;
            const end = start + chunkSize
            const packet = file.slice(start, end); // 文件进行切片
            const formData = utils.objToFormData({
                fileHash: hash,
                hash: `${hash}-${index}`,
                chunk: packet,
                name: name,
                totalSize: size,
                total: chunkCount, // 总切片数
                index: index+1 // 当前切片
            })
            return new Promise(resolve => {
                ApiWrapper(`切片${index}上传`, uploadChunks, formData).then(data => {
                    resolve(data) // 拿到已上传过的切片
                }).catch(() => {
                    resolve({uploadList: []})
                })
            })
        }

        /**
         * 上传切片
         * @param file
         * @return {Promise<void>}
         */
        async uploadWalk(file) {
            const { name, size } = file;
            const hash = await this.calculateFileMd5ByDefaultChunkSize(file);
            const chunkCount = Math.ceil(size / CHUNK_SIZE);
            progressEl.max = size // 总进度
            // 验证当前文件是否已经上传
            const { uploaded } = await ApiWrapper("验证切片", verifyChunk, {hash: hash, total: chunkCount })

            // 如果已经上传了切片，直接合并切片
            if (uploaded) {
                return this.mergeChunks({ hash, filename: name, size}) // 合并切片
            }

            for (let i = 0; i < chunkCount; i++) {
                const { progress } = await this.postFile(file, i, hash, CHUNK_SIZE)
                progressEl.value = i * CHUNK_SIZE // 设置进度条
            }

            await this.mergeChunks({ hash, filename: name, size}) // 合并切片
            fileInput.value = null
        }

        /**
         * 合并切片
         * @return {Promise<void>}
         */
        async mergeChunks(data) {
            const { hash, filename, size } = data
            return ApiWrapper('合并切片', mergeChunks, {
                name:filename,
                hash: hash,
                size: size
            }).then(data => {
                progressEl.value = 0;
                console.log(data)
            }).catch(err => {
                console.log(err)
            })
        }

        /**
         * 上传事件
         */
        async onUploadFile() {
            const [file] = fileInput.files;
            if (!file) {
                messageEl.innerHTML = MESSAGE_MAP.NO_FILE;
                return;
            }
            const errMessage = this.validateFile(file); // 验证文件
            if (errMessage) {
                messageEl.innerHTML = errMessage;
                return;
            }
            this.uploadWalk(file); // 执行上传逻辑
        }

        /**
         * 暂停上传
         */
        onPauseUpload(e) {
            this.setUploadStatus(UPLOAD_STATUS.PAUSE) // 暂停上传
        }
    }

    // export
    return new FileChunkUpload()
})(document);
